import _mergeJSXProps2 from '@vue/babel-helper-vue-jsx-merge-props'
import _mergeJSXProps from '@vue/babel-helper-vue-jsx-merge-props'
import _extends from '@babel/runtime/helpers/esm/extends'
// Utils
import { resetScroll } from '../utils/dom/reset-scroll'
import { formatNumber } from '../utils/format/number'
import { preventDefault } from '../utils/dom/event'
import {
  isDef,
  addUnit,
  isObject,
  isPromise,
  isFunction,
  createNamespace
} from '../utils' // Components
import './style'
import Icon from '../icon'
import Cell from '../cell'
import { cellProps } from '../cell/shared'

var _createNamespace = createNamespace('field')
var createComponent = _createNamespace[0]
var bem = _createNamespace[1]

export default createComponent({
  inheritAttrs: false,
  provide: function provide() {
    return {
      vanField: this
    }
  },
  inject: {
    vanForm: {
      default: null
    }
  },
  props: _extends({}, cellProps, {
    name: String,
    rules: Array,
    disabled: {
      type: Boolean,
      default: null
    },
    readonly: {
      type: Boolean,
      default: null
    },
    autosize: [Boolean, Object],
    leftIcon: String,
    rightIcon: String,
    clearable: Boolean,
    formatter: Function,
    maxlength: [Number, String],
    labelWidth: [Number, String],
    labelClass: null,
    labelAlign: String,
    inputAlign: String,
    placeholder: String,
    errorMessage: String,
    errorMessageAlign: String,
    showWordLimit: Boolean,
    value: {
      type: [Number, String],
      default: ''
    },
    type: {
      type: String,
      default: 'text'
    },
    error: {
      type: Boolean,
      default: null
    },
    colon: {
      type: Boolean,
      default: null
    },
    clearTrigger: {
      type: String,
      default: 'focus'
    },
    formatTrigger: {
      type: String,
      default: 'onChange'
    }
  }),
  data: function data() {
    return {
      focused: false,
      validateFailed: false,
      validateMessage: ''
    }
  },
  watch: {
    value: function value() {
      this.updateValue(this.value)
      this.resetValidation()
      this.validateWithTrigger('onChange')
      this.$nextTick(this.adjustSize)
    }
  },
  mounted: function mounted() {
    this.updateValue(this.value, this.formatTrigger)
    this.$nextTick(this.adjustSize)

    if (this.vanForm) {
      this.vanForm.addField(this)
    }
  },
  beforeDestroy: function beforeDestroy() {
    if (this.vanForm) {
      this.vanForm.removeField(this)
    }
  },
  computed: {
    showClear: function showClear() {
      var readonly = this.getProp('readonly')

      if (this.clearable && !readonly) {
        var hasValue = isDef(this.value) && this.value !== ''
        var trigger =
          this.clearTrigger === 'always' ||
          (this.clearTrigger === 'focus' && this.focused)
        return hasValue && trigger
      }
    },
    showError: function showError() {
      if (this.error !== null) {
        return this.error
      }

      if (this.vanForm && this.vanForm.showError && this.validateFailed) {
        return true
      }
    },
    listeners: function listeners() {
      return _extends({}, this.$listeners, {
        blur: this.onBlur,
        focus: this.onFocus,
        input: this.onInput,
        click: this.onClickInput,
        keypress: this.onKeypress
      })
    },
    labelStyle: function labelStyle() {
      var labelWidth = this.getProp('labelWidth')

      if (labelWidth) {
        return {
          width: addUnit(labelWidth)
        }
      }
    },
    formValue: function formValue() {
      if (this.children && (this.$scopedSlots.input || this.$slots.input)) {
        return this.children.value
      }

      return this.value
    }
  },
  methods: {
    // @exposed-api
    focus: function focus() {
      if (this.$refs.input) {
        this.$refs.input.focus()
      }
    },
    // @exposed-api
    blur: function blur() {
      if (this.$refs.input) {
        this.$refs.input.blur()
      }
    },
    runValidator: function runValidator(value, rule) {
      return new Promise(function(resolve) {
        var returnVal = rule.validator(value, rule)

        if (isPromise(returnVal)) {
          return returnVal.then(resolve)
        }

        resolve(returnVal)
      })
    },
    isEmptyValue: function isEmptyValue(value) {
      if (Array.isArray(value)) {
        return !value.length
      }

      if (value === 0) {
        return false
      }

      return !value
    },
    runSyncRule: function runSyncRule(value, rule) {
      if (rule.required && this.isEmptyValue(value)) {
        return false
      }

      if (rule.pattern && !rule.pattern.test(value)) {
        return false
      }

      return true
    },
    getRuleMessage: function getRuleMessage(value, rule) {
      var message = rule.message

      if (isFunction(message)) {
        return message(value, rule)
      }

      return message
    },
    runRules: function runRules(rules) {
      var _this = this

      return rules.reduce(function(promise, rule) {
        return promise.then(function() {
          if (_this.validateFailed) {
            return
          }

          var value = _this.formValue

          if (rule.formatter) {
            value = rule.formatter(value, rule)
          }

          if (!_this.runSyncRule(value, rule)) {
            _this.validateFailed = true
            _this.validateMessage = _this.getRuleMessage(value, rule)
            return
          }

          if (rule.validator) {
            return _this.runValidator(value, rule).then(function(result) {
              if (result === false) {
                _this.validateFailed = true
                _this.validateMessage = _this.getRuleMessage(value, rule)
              }
            })
          }
        })
      }, Promise.resolve())
    },
    validate: function validate(rules) {
      var _this2 = this

      if (rules === void 0) {
        rules = this.rules
      }

      return new Promise(function(resolve) {
        if (!rules) {
          resolve()
        }

        _this2.resetValidation()

        _this2.runRules(rules).then(function() {
          if (_this2.validateFailed) {
            resolve({
              name: _this2.name,
              message: _this2.validateMessage
            })
          } else {
            resolve()
          }
        })
      })
    },
    validateWithTrigger: function validateWithTrigger(trigger) {
      if (this.vanForm && this.rules) {
        var defaultTrigger = this.vanForm.validateTrigger === trigger
        var rules = this.rules.filter(function(rule) {
          if (rule.trigger) {
            return rule.trigger === trigger
          }

          return defaultTrigger
        })

        if (rules.length) {
          this.validate(rules)
        }
      }
    },
    resetValidation: function resetValidation() {
      if (this.validateFailed) {
        this.validateFailed = false
        this.validateMessage = ''
      }
    },
    updateValue: function updateValue(value, trigger) {
      if (trigger === void 0) {
        trigger = 'onChange'
      }

      value = isDef(value) ? String(value) : '' // native maxlength have incorrect line-break counting
      // see: https://github.com/youzan/vant/issues/5033

      var maxlength = this.maxlength

      if (isDef(maxlength) && value.length > maxlength) {
        if (this.value && this.value.length === +maxlength) {
          value = this.value
        } else {
          value = value.slice(0, maxlength)
        }
      }

      if (this.type === 'number' || this.type === 'digit') {
        var isNumber = this.type === 'number'
        value = formatNumber(value, isNumber, isNumber)
      }

      if (this.formatter && trigger === this.formatTrigger) {
        value = this.formatter(value)
      }

      var input = this.$refs.input

      if (input && value !== input.value) {
        input.value = value
      }

      if (value !== this.value) {
        this.$emit('input', value)
      }
    },
    onInput: function onInput(event) {
      // not update v-model when composing
      if (event.target.composing) {
        return
      }

      this.updateValue(event.target.value)
    },
    onFocus: function onFocus(event) {
      this.focused = true
      this.$emit('focus', event) // readonly not work in lagacy mobile safari

      /* istanbul ignore if */

      var readonly = this.getProp('readonly')

      if (readonly) {
        this.blur()
      }
    },
    onBlur: function onBlur(event) {
      this.focused = false
      this.updateValue(this.value, 'onBlur')
      this.$emit('blur', event)
      this.validateWithTrigger('onBlur')
      resetScroll()
    },
    onClick: function onClick(event) {
      this.$emit('click', event)
    },
    onClickInput: function onClickInput(event) {
      this.$emit('click-input', event)
    },
    onClickLeftIcon: function onClickLeftIcon(event) {
      this.$emit('click-left-icon', event)
    },
    onClickRightIcon: function onClickRightIcon(event) {
      this.$emit('click-right-icon', event)
    },
    onClear: function onClear(event) {
      preventDefault(event)
      this.$emit('input', '')
      this.$emit('clear', event)
    },
    onKeypress: function onKeypress(event) {
      var ENTER_CODE = 13

      if (event.keyCode === ENTER_CODE) {
        var submitOnEnter = this.getProp('submitOnEnter')

        if (!submitOnEnter && this.type !== 'textarea') {
          preventDefault(event)
        } // trigger blur after click keyboard search button

        if (this.type === 'search') {
          this.blur()
        }
      }

      this.$emit('keypress', event)
    },
    adjustSize: function adjustSize() {
      var input = this.$refs.input

      if (!(this.type === 'textarea' && this.autosize) || !input) {
        return
      }

      input.style.height = 'auto'
      var height = input.scrollHeight

      if (isObject(this.autosize)) {
        var _this$autosize = this.autosize
        var maxHeight = _this$autosize.maxHeight
        var minHeight = _this$autosize.minHeight

        if (maxHeight) {
          height = Math.min(height, maxHeight)
        }

        if (minHeight) {
          height = Math.max(height, minHeight)
        }
      }

      if (height) {
        input.style.height = height + 'px'
      }
    },
    genInput: function genInput() {
      var h = this.$createElement
      var type = this.type
      var disabled = this.getProp('disabled')
      var readonly = this.getProp('readonly')
      var inputSlot = this.slots('input')
      var inputAlign = this.getProp('inputAlign')

      if (inputSlot) {
        return h(
          'div',
          {
            class: bem('control', [inputAlign, 'custom']),
            on: {
              click: this.onClickInput
            }
          },
          [inputSlot]
        )
      }

      var inputProps = {
        ref: 'input',
        class: bem('control', inputAlign),
        domProps: {
          value: this.value
        },
        attrs: _extends({}, this.$attrs, {
          name: this.name,
          disabled: disabled,
          readonly: readonly,
          placeholder: this.placeholder
        }),
        on: this.listeners,
        // add model directive to skip IME composition
        directives: [
          {
            name: 'model',
            value: this.value
          }
        ]
      }

      if (type === 'textarea') {
        return h('textarea', _mergeJSXProps([{}, inputProps]))
      }

      var inputType = type
      var inputMode // type="number" is weired in iOS, and can't prevent dot in Android
      // so use inputmode to set keyboard in mordern browers

      if (type === 'number') {
        inputType = 'text'
        inputMode = 'decimal'
      }

      if (type === 'digit') {
        inputType = 'tel'
        inputMode = 'numeric'
      }

      return h(
        'input',
        _mergeJSXProps2([
          {
            attrs: {
              type: inputType,
              inputmode: inputMode
            }
          },
          inputProps
        ])
      )
    },
    genLeftIcon: function genLeftIcon() {
      var h = this.$createElement
      var showLeftIcon = this.slots('left-icon') || this.leftIcon

      if (showLeftIcon) {
        return h(
          'div',
          {
            class: bem('left-icon'),
            on: {
              click: this.onClickLeftIcon
            }
          },
          [
            this.slots('left-icon') ||
              h(Icon, {
                attrs: {
                  name: this.leftIcon,
                  classPrefix: this.iconPrefix
                }
              })
          ]
        )
      }
    },
    genRightIcon: function genRightIcon() {
      var h = this.$createElement
      var slots = this.slots
      var showRightIcon = slots('right-icon') || this.rightIcon

      if (showRightIcon) {
        return h(
          'div',
          {
            class: bem('right-icon'),
            on: {
              click: this.onClickRightIcon
            }
          },
          [
            slots('right-icon') ||
              h(Icon, {
                attrs: {
                  name: this.rightIcon,
                  classPrefix: this.iconPrefix
                }
              })
          ]
        )
      }
    },
    genWordLimit: function genWordLimit() {
      var h = this.$createElement

      if (this.showWordLimit && this.maxlength) {
        var count = (this.value || '').length
        return h(
          'div',
          {
            class: bem('word-limit')
          },
          [
            h(
              'span',
              {
                class: bem('word-num')
              },
              [count]
            ),
            '/',
            this.maxlength
          ]
        )
      }
    },
    genMessage: function genMessage() {
      var h = this.$createElement

      if (this.vanForm && this.vanForm.showErrorMessage === false) {
        return
      }

      var message = this.errorMessage || this.validateMessage

      if (message) {
        var errorMessageAlign = this.getProp('errorMessageAlign')
        return h(
          'div',
          {
            class: bem('error-message', errorMessageAlign)
          },
          [message]
        )
      }
    },
    getProp: function getProp(key) {
      if (isDef(this[key])) {
        return this[key]
      }

      if (this.vanForm && isDef(this.vanForm[key])) {
        return this.vanForm[key]
      }
    },
    genLabel: function genLabel() {
      var h = this.$createElement
      var colon = this.getProp('colon') ? ':' : ''

      if (this.slots('label')) {
        return [this.slots('label'), colon]
      }

      if (this.label) {
        return h('span', [this.label + colon])
      }
    }
  },
  render: function render() {
    var _bem

    var h = arguments[0]
    var slots = this.slots
    var disabled = this.getProp('disabled')
    var labelAlign = this.getProp('labelAlign')
    var scopedSlots = {
      icon: this.genLeftIcon
    }
    var Label = this.genLabel()

    if (Label) {
      scopedSlots.title = function() {
        return Label
      }
    }

    var extra = this.slots('extra')

    if (extra) {
      scopedSlots.extra = function() {
        return extra
      }
    }

    return h(
      Cell,
      {
        attrs: {
          icon: this.leftIcon,
          size: this.size,
          center: this.center,
          border: this.border,
          isLink: this.isLink,
          required: this.required,
          clickable: this.clickable,
          titleStyle: this.labelStyle,
          valueClass: bem('value'),
          titleClass: [bem('label', labelAlign), this.labelClass],
          arrowDirection: this.arrowDirection
        },
        scopedSlots: scopedSlots,
        class: bem(
          ((_bem = {
            error: this.showError,
            disabled: disabled
          }),
          (_bem['label-' + labelAlign] = labelAlign),
          (_bem['min-height'] = this.type === 'textarea' && !this.autosize),
          _bem)
        ),
        on: {
          click: this.onClick
        }
      },
      [
        h(
          'div',
          {
            class: bem('body')
          },
          [
            this.genInput(),
            this.showClear &&
              h(Icon, {
                attrs: {
                  name: 'clear'
                },
                class: bem('clear'),
                on: {
                  touchstart: this.onClear
                }
              }),
            this.genRightIcon(),
            slots('button') &&
              h(
                'div',
                {
                  class: bem('button')
                },
                [slots('button')]
              )
          ]
        ),
        this.genWordLimit(),
        this.genMessage()
      ]
    )
  }
})
